#include "Coconut.h"
#include <iostream>
#include <ft2build.h>
#include "time.h"

/*
* BY: MARCUS BELCHER
*     072004612
* BASED ON: RAGDOLL.CPP
* USES CYCLONE PHYSICS ENGINE
*
*/

GLfloat floorMirrorTranspose[16] = 
{
    1, 0, 0, 0,
    0, -1, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1
};

//GAME VARIBLES 
clock_t programStart;
clock_t programEnd;
const static int player_amount = 5;
static int hittable_enemy_amount = 5;
static float boxHeight = 1;
static bool EnemyChosen = false;
static bool greset = false;
static float delta = 0;
bool playerVelocityCap = false; 
bool playerZpositionCap = false;
bool playerYpositionCap = false;
bool ballHitSomething = true;
bool BONE_COLLIDE = false;
int lives = 5;
int level = 1;
float floor_tar_movement = 3.0f;
bool floor_tar = false;
float floor_ice_movement = 0.5f;
bool floor_ice = false;
float floor_normal_movement = 1.0f;
bool floor_normal = true;

//RAGDOLL POSITION SETUP
const float bone_position[12][3] =	{{0, 0.993, -0.5},{0, 3.159, -0.56},{0, 0.993, 0.5},{0, 3.15, 0.56},
								{-0.054, 4.683, 0.013},{0.043, 5.603, 0.013},{0, 6.485, 0.013},{0, 7.759, 0.013},
								{0, 5.946, -1.066},{0, 4.024, -1.066},{0, 5.946, 1.066},{0, 4.024, 1.066}};

const float bone_external[12][3] =	{{0.301, 1.0, 0.234},{0.301, 1.0, 0.234},{0.301, 1.0, 0.234},{0.301, 1.0, 0.234},
								{0.415, 0.392, 0.690},{0.301, 0.367, 0.693},{0.435, 0.367, 0.786},{0.45, 0.598, 0.421},
								{0.267, 0.888, 0.207},{0.267, 0.888, 0.207},{0.267, 0.888, 0.207},{0.267, 0.888, 0.207}};

const float joint_position[11][3] =	{{0, 1.07f, 0},{0, 1.07f, 0},{0, 0.96f, 0},{0, 0.96f, 0},
								{0.054f, 0.50f, 0},{-0.043f, 0.411f, 0},{0, 0.521f, 0},{0, 1.066f, 0},
								{0, 1.066f, 0},{0, 0.367f, -0.8f},{0, 0.367f, 0.8f}};

const float joint_external[11][3] =	{{0, -1.07f, 0},{0, -1.07f, 0},{0, -0.96f, 0},{0, -0.96f, 0},
								{-0.043f, -0.45f, 0},{0, -0.411f, 0},{0, -0.752f, 0},{0, -0.458f, -0.5f},
								{0, -0.458f, 0.5f},{0, 0.888f, 0.32f},{0, 0.888f, -0.32f}};

const int joint_body_one[11] = { 0, 2, 9, 11, 4, 5, 6, 1, 3, 6, 6};
const int joint_body_two[11] = { 1, 3, 8, 10, 5, 6, 7, 4, 4, 8, 10};

//USED POSITIONS
vector<float> used_ENEMY_X;
vector<float> used_ENEMY_Z;
//ENEMY POSITIONS
const static int ENEMY_AMOUNT = 10;
const static float ENEMY_X[ENEMY_AMOUNT]  = {10, 8,6,4,2, 2, 4, 6, 8, 10};
const static float ENEMY_Z[ENEMY_AMOUNT]  = {-15, -12,-9,-6,-3, 3, 6, 9, 12, 15};
//SNOW STRUCT, HOLDS ARRAY OF MULTIPLE BALL OBJECTS
struct SNOW{
	const static int y = 3;
	const static int x = 5;
	const static int z = 5;
	Ball* snow[y][x][z];
	bool snowing;
};

SNOW *snow;


CoconutGame::CoconutGame(): RigidBodyApplication(), editMode(false), upMode(false), playerVelocity(0.0), overallPlayerHits(0),
	playerZposition(0.0), playerYposition(0.0), playerScore(0), playerMisses(0), playerHits(0), HEADSHOT(false), ballcounter(0)
{   
	greset = (false);
	reset();//AUTO INIT
	programStart = clock();
	snow = new SNOW;
}

const char* CoconutGame::getTitle(){ 
    return "Dodgeball!!!"; 
}

void CoconutGame::fire(){
    cyclone::Vector3 pos = ballData[0].body->getPosition();
    pos.normalise();

    ballData[0].body->addForce(pos * -1000.0f);
}

//SIMPLE PAUSE FUNCTION (USED TO STALL INPUT ETC)
void pause(float seconds){
  clock_t endwait;
  endwait = clock() + seconds * CLOCKS_PER_SEC;
  while (clock() < endwait){
  }
}


void CoconutGame::reset(){
	
	//CLEAR OLD OBJECTS
	//AND RESET
	used_ENEMY_X.clear();
	used_ENEMY_Z.clear();
	editMode = false;
	upMode = false; 
	playerVelocity = (0.0); 
	playerZposition = (0.0); 
	playerYposition = (0.0); 
	HEADSHOT =(false); 
	ballcounter = (0);
	EnemyChosen = false;
	std::cout << "RESET DONE\n";

	if(greset){
		//AFTER FIRST LEVEL FROM INITIAL LOAD
		//CYCLE THROUGH LEVELS
		if(floor_ice){
			floor_normal = true;
			floor_tar = false;
			floor_ice = false;
		} else if (floor_normal){
			floor_normal = false;
			floor_tar = false;
			floor_ice = true;
		} else if (floor_tar){
			floor_normal = false;
			floor_tar = false;
			floor_ice = true;
		}
		//IF ICE LEVEL INIT SNOWBALLS
		//SNOW IS A [][][] ARRAY 
		if(floor_ice){
			cyclone::Random random;
			for(int i = 0;i<snow->y;i++){
				for(int k = 0; k < snow->x;k++){
					for(int j = 0; j < snow->z;j++){
						snow->snow[i][k][j] = new Ball();
						cyclone::Vector3 pos(random.randomReal(0,10)-10.0f,random.randomReal(20,200),random.randomReal(0,40)-20.0f);//random.randomReal(10,20)
						cyclone::Vector3 vel(0,0,0);
						snow->snow[i][k][j]->setState(	pos,
														cyclone::Quaternion(),
														0.25f,
														vel);
						snow->snow[i][k][j]->body->setAcceleration(cyclone::Vector3(0,-6,0));
					}
				}
			}
		}
		//RESETS THE REST OF THE LEVEL
		std::cout << "greset: RESET DONE\n";
		overallPlayerHits += playerHits;
		playerHits = 0;
		enemies_Bones->clear();
		enemies_Joints->clear();
		greset = false;
		hittable_enemy_amount--;
		level++;
		boxHeight+= 0.5;

		//IF END OF GAME RESET 
		if(hittable_enemy_amount <= 0){
			//new game
			hittable_enemy_amount = 5;
			playerScore = (0); 
			playerMisses = (0); 
			playerHits = (0);
			overallPlayerHits = 0;
			boxHeight = 1;
			level = 0;
			lives = 5;
		}
	}

	//INIT ENEMIES
	enemies_Bones = new vector<vector<Bone*>*>;
	enemies_Joints = new vector<vector<cyclone::Joint*>*>;
	greset = true;

	cyclone::Random random;
	float vel_multiply = 0;
	if(floor_normal)vel_multiply = floor_normal_movement;
	else if(floor_tar)vel_multiply = floor_tar_movement;
	else if(floor_ice)vel_multiply = floor_ice_movement;

		for(int i = 0; i < player_amount;i++){
			bool randomNUm = false;
			int counter = 0;
			int ran = random.randomInt(10);
			int ran2 = random.randomInt(10);
			int ran3 = random.randomInt(10);
			int ran4 = random.randomInt(10);
			//ENEMIES ALL HAVE RANDOM START N END, EXTRA CHECK 
			//TO STOP MULTIPLE ENEMIES STARTING AT THE SAME SPAWN
			for(int i = 0; i < used_ENEMY_X.size();i++){
				if(ran == used_ENEMY_X.at(i) && ran2 == used_ENEMY_Z.at(i)){
					ran = random.randomInt(10);
					ran2 = random.randomInt(10);
				}
			}
		/*	float ranX = random.randomInt(5);
			float ranZ = random.randomInt(5);
			int invertNum = random.randomInt(10);
			if(invertNum %2 == 0)invertNum = 1;
			else invertNum = -1;*/

			enemy_Bones = new vector<Bone*>;
			cyclone::Vector3 veloo = random.randomVector(5.0f);
			veloo.y = 0;
			for(int k = 0; k < 12;k++){
				cyclone::Vector3 veloo;
				//SET UP BONES TO SPECIFIC INIT LOCATIONS
				//ALSO SET RAGDOLL TO NOT FALL IN GRAVETY
				Bone *bone = new Bone();
				bone->setState(
					cyclone::Vector3(bone_position[k][0]-ENEMY_X[ran],bone_position[k][1],bone_position[k][2]-ENEMY_Z[ran2]), 
					cyclone::Vector3(bone_external[k][0],bone_external[k][1],bone_external[k][2]));

				bone->atDestination(cyclone::Vector3(bone_position[k][0]-ENEMY_X[ran3],bone_position[k][1],bone_position[k][2]-ENEMY_Z[ran4]));
				bone->setVelocity(cyclone::Vector3(( bone->chosenDestination.x-bone->body->getPosition().x)/(bone->max_moveamount*vel_multiply),
								bone->body->getPosition().y,
								(bone->chosenDestination.z-bone->body->getPosition().z)/(bone->max_moveamount*vel_multiply)));

				bone->body->setAcceleration(cyclone::Vector3());//NO ACCELLERATION (NO FALL)
				enemy_Bones->push_back(bone);
				used_ENEMY_Z.push_back(ENEMY_Z[ran2]);
				used_ENEMY_X.push_back(ENEMY_X[ran]);

				if(hittable_enemy_amount >= i+1){
					bone->chosenEnemy = true;
					//std::cout << "set enemy to hittable\n" << "hittable_enemy_amount " << hittable_enemy_amount << " k " << k << " i " << i+1 <<"\n";
				}
				else bone->chosenEnemy = false;
			}
			enemies_Bones->push_back(enemy_Bones);

			//INIT ENEMY JOINTS
			enemy_Joints = new vector<cyclone::Joint*>;
			for(int k = 0; k < 11;k++){
				cyclone::Joint *tjoint = new cyclone::Joint();
				tjoint->set(
				enemy_Bones->at(joint_body_one[k])->body, cyclone::Vector3(joint_position[k][0],joint_position[k][1],joint_position[k][2]),
				enemy_Bones->at(joint_body_two[k])->body, cyclone::Vector3(joint_external[k][0],joint_external[k][1],joint_external[k][2]),
				0.15f
				);

				enemy_Joints->push_back(tjoint);
			}
			enemies_Joints->push_back(enemy_Joints);
	}

	//INIT AND SET UP GAME BOXES(AKA WALLED AREA)
	int boxtemp[3] = {1,1,22};
	int boxtemp2[3] = {6,6,1};
	float boxPos[3][3] = {{-5,1,-21},{-5,1,21},{-12,1,0}}; 
	int boxC = 0;
	for (Box *box = boxData; box < boxData+boxes; box++){
		box->body->setAcceleration(cyclone::Vector3());
		cyclone::Vector3 pos(boxPos[boxC][0],boxHeight,boxPos[boxC][2]);
		cyclone::Vector3 vel(0,0,0);
		box->setState(
			pos,
			cyclone::Quaternion(),
			cyclone::Vector3(boxtemp2[boxC],boxHeight,boxtemp[boxC]),
            vel
        );
		boxC++;
	}
	//INIT DODGEBALLS
	int ballZtemp[5] = {8,4,0,-4,-4};
	int ballZtempC = 0;
    for (Ball *ball = ballData; ball < ballData+balls; ball++){
		cyclone::Vector3 pos(20,0,ballZtemp[2]);
		cyclone::Vector3 vel(0,0,0);
		ball->setState(
			pos,
			cyclone::Quaternion(),
            1.0f,
            vel
            );
		ball->body->setAcceleration(cyclone::Vector3());
		ballZtempC++;
		ball->BALL_COLLIDE = false;
		if(ballZtempC >= 5)ballZtempC = 0;
    }

	// Reset the contacts
	cData.contactCount = 0;
	pause(0.5);
}

void CoconutGame::generateContacts(){
	// Note that this method makes a lot of use of early returns to avoid 
	// processing lots of potential contacts that it hasn't got room to 
	// store.

    // Create the ground plane data
    cyclone::CollisionPlane plane;
    plane.direction = cyclone::Vector3(0,1,0);
    plane.offset = 0;

    // Set up the collision data structure
    cData.reset(maxContacts);
    cData.friction = (cyclone::real)0.9;
    cData.restitution = (cyclone::real)0.6;
    cData.tolerance = (cyclone::real)0.1;

    // Perform exhaustive collision detection
    cyclone::Matrix4 transform, otherTransform;
    cyclone::Vector3 position, otherPosition;

	//FOR EVERY SET OF BONES (AKA BODY)
	for(int i = 0; i < enemies_Bones->size(); i++){
		BONE_COLLIDE = false;
		for (int k = 0; k < enemies_Bones->at(i)->size(); k ++){//FOR EACH BONE IN THE BODY
			Bone *bone = enemies_Bones->at(i)->at(k);//PERFORM CHECK ON BODY PART(BONE)
			// Check for collisions with the ground plane
			if (!cData.hasMoreContacts()) return;
			cyclone::CollisionDetector::boxAndHalfSpace(*bone, plane, &cData);
			cyclone::CollisionSphere boneSphere = bone->getCollisionSphere();

			// Check for collisions with each other box
			for (int l = 0; l < enemies_Bones->at(i)->size(); l ++){
				if(l != k){//DOESNT CHECK AGAINST ITSELF
					Bone *other = enemies_Bones->at(i)->at(l);
					if (!cData.hasMoreContacts()) return;
					//creates a bounding sphere other the bone
					cyclone::CollisionSphere otherSphere = other->getCollisionSphere();
					//detects collision between other bones
					cyclone::CollisionDetector::sphereAndSphere(boneSphere, otherSphere, &cData);
				}
			}

			if(floor_ice){//ICE LEVEL
				for(int i = 0;i<snow->y;i++){
					for(int k = 0; k < snow->x;k++){
						for(int j = 0; j < snow->z;j++){
							if(HEADSHOT){//ONLY IF THE PLAYER SCORED A HEADSHOT
								if(cyclone::CollisionDetector::boxAndSphere(*bone, *snow->snow[i][k][j], &cData)){
								//ENEMY HIT
								std::cout << "SNOW COLLISION WITH BONE\n";
								playerHits++;
								playerScore++;
								if(k == 7){
									playerScore++;
									HEADSHOT = true;
									lives++;
								}
								bone->chosenEnemy = false;
								bone->body->setVelocity(snow->snow[i][k][j]->body->getVelocity());
								//ADD GRAVITY TO RAGDOLL
									for(int j = 0; j < enemies_Bones->at(i)->size();j++){
										std::cout << i << " " << j << "\n";
										enemies_Bones->at(i)->at(j)->body->setAcceleration(cyclone::Vector3::GRAVITY);
										enemies_Bones->at(i)->at(j)->BONE_COLLIDE = true;
									}
								}
							}
						}
					}
				}
			}
		}
	}


    // Check for joint violation
	for(int i = 0; i < enemies_Joints->size(); i++){
		for(int j = 0; j < enemies_Joints->at(i)->size(); j++){
			cyclone::Joint *joint = enemies_Joints->at(i)->at(j);
			if (!cData.hasMoreContacts()) return;
		    unsigned added = joint->addContact(cData.contacts, cData.contactsLeft);
			cData.addContacts(added);
		}
	}

	//CHECK ALL BOXES(AKA WALLED ENVIROMENT
    for (Box *box = boxData; box < boxData+boxes; box++){

		for(int i = 0; i < enemies_Bones->size(); i++){
			for (int k = 0; k < enemies_Bones->at(i)->size(); k ++){
				//CHECK RAGDOLLS WITH THE WALLS
				Bone *bone = enemies_Bones->at(i)->at(k);
				if(!cData.hasMoreContacts()) return;
				cyclone::CollisionDetector::boxAndBox(*box, *bone, &cData);
			}
		}

		// Check for collisions with each ball
        for (Ball *other = ballData; other < ballData+balls; other++){ 
    		if (!cData.hasMoreContacts()) return;
			if(cyclone::CollisionDetector::boxAndSphere(*box, *other, &cData)) ballHitSomething= true;
		}
    }

    for (Ball *ball = ballData; ball < ballData+balls; ball++){
        // Check for collisions with the ground plane
        if (!cData.hasMoreContacts()) return;
		if(cyclone::CollisionDetector::sphereAndHalfSpace(*ball, plane, &cData)) ballHitSomething= true;

        for (Ball *other = ball+1; other < ballData+balls; other++){
            // Check for collisions with the ground plane
    		if (!cData.hasMoreContacts()) return;
            if(cyclone::CollisionDetector::sphereAndSphere(*ball, *other, &cData)) ballHitSomething= true;
        }

		//CHECK BALL WITH SNOW
		if(floor_ice){
			for(int i = 0;i<snow->y;i++){
				for(int k = 0; k < snow->x;k++){
					for(int j = 0; j < snow->z;j++){
						if(cyclone::CollisionDetector::sphereAndSphere(*ball, *snow->snow[i][k][j], &cData)) ballHitSomething= true;
					}
				}
			}
		}

		//CHECK BALL WITH BONES(ALL RAGDOLL BODYS)
		for(int i = 0; i < enemies_Bones->size(); i++){
			ball->BALL_COLLIDE = false;
			for (int k = 0; k < enemies_Bones->at(i)->size(); k ++){
				Bone *bone = enemies_Bones->at(i)->at(k);
				if(!cData.hasMoreContacts()) return;
				cyclone::CollisionSphere boneSphere = bone->getCollisionSphere();
				if(cyclone::CollisionDetector::boxAndSphere(*bone, *ball, &cData)){
					if(!ball->BALL_COLLIDE && !bone->BONE_COLLIDE){
						//COLLIDED WITH HITTABLE ENEMY
						if(bone->chosenEnemy){
							//std::cout << "BALL COLLISION WITH BONE\n";
							ball->BALL_COLLIDE = true;
							playerHits++;
							playerScore++;
							HEADSHOT = false;
							if(k == 7){//IF THE HEAD OF THE RAGDOLL
								playerScore++;
								HEADSHOT = true;
								lives++;
							}
							bone->chosenEnemy = false;
							bone->body->setVelocity(ball->body->getVelocity());
							for(int j = 0; j < enemies_Bones->at(i)->size();j++){
								//std::cout << i << " " << j << "\n";
								//enemies_Bones->at(i)->at(j)->body->setVelocity(ball->body->getVelocity());
								enemies_Bones->at(i)->at(j)->body->setAcceleration(cyclone::Vector3::GRAVITY);
								enemies_Bones->at(i)->at(j)->BONE_COLLIDE = true;//SETS THE PART OF BODY A DIFFERENT RENDER COLOUR
							}													 //THUS PLAYER CAN SEE WHERE HE/SHE HITS
						}
						else{
							//ELSE IF PLAYER NOT HITTABLE LOOSE POINTS
							//std::cout << "non enemy: BALL COLLISION WITH BONE\n";
							ball->BALL_COLLIDE = true;
							playerMisses++;
							playerScore--;
							lives--;
							HEADSHOT = false;
							if(k == 7){//IF THE HEAD OF THE RAGDOLL
								lives--;
							}
							bone->chosenEnemy = false;
							bone->body->setVelocity(ball->body->getVelocity());
							for(int j = 0; j < enemies_Bones->at(i)->size();j++){
								std::cout << i << " " << j << "\n";
								enemies_Bones->at(i)->at(j)->body->setAcceleration(cyclone::Vector3::GRAVITY);
								enemies_Bones->at(i)->at(j)->BONE_COLLIDE = true;
							}
						}
					}
					else if(!bone->BONE_COLLIDE){
						//std::cout << "collision but bone is false\n";
					}
				}
			}
		}
    }
}

void CoconutGame::updateObjects(cyclone::real duration)
{	
	cyclone::Random random;
	if(playerHits >= hittable_enemy_amount)reset();
	if(lives <= 0)reset();
///>ExplosionUpdate
    // Update the physics of each box in turn
    for (Box *box = boxData; box < boxData+boxes; box++){
        // Run the physics
        //box->body->integrate(duration);//commented out to stop collision with itself (stops it moving)
        box->calculateInternals();
        box->isOverlapping = false;
    }

    // Update the physics of each ball in turn
    for (Ball *ball = ballData; ball < ballData+balls; ball++){
        // Run the physics
        ball->body->integrate(duration);
        ball->calculateInternals();
    }

	//UPDATE SNOW
	if(floor_ice){
		for(int i = 0;i<snow->y;i++){
			for(int k = 0; k < snow->x;k++){
				for(int j = 0; j < snow->z;j++){
					if(snow->snow[i][k][j]->body->getPosition().y <= 0.25f){
						snow->snow[i][k][j]->body->setPosition(	snow->snow[i][k][j]->body->getPosition().x, 
																random.randomReal(20,200), 
																snow->snow[i][k][j]->body->getPosition().z);
					}
					snow->snow[i][k][j]->body->integrate(duration);
					snow->snow[i][k][j]->calculateInternals();
				}
			}
		}
	}

	//UPDATE ALL RAGDOLLS
	for(int i = 0; i < enemies_Bones->size();i++){

		int ran3 = random.randomInt(10);
		int ran4 = random.randomInt(10);
		float vel_multiply = 0;
		if(floor_normal)vel_multiply = floor_normal_movement;
		else if(floor_tar)vel_multiply = floor_tar_movement;
		else if(floor_ice)vel_multiply = floor_ice_movement;
		cyclone::Vector3 veloo = random.randomXZVector(1);
			for(int k = 0; k < 12;k++){
				//UPDATE ENEMIES TO NEW POSITION IF THEY HAVENT BEEN HIT
					Bone *bone = enemies_Bones->at(i)->at(k);
					if(!enemies_Bones->at(i)->at(0)->BONE_COLLIDE){
					if(bone->moveamount < bone->max_moveamount*vel_multiply){
						cyclone::Vector3 newPos = bone->body->getPosition()+bone->getVelocity();
						newPos.y = bone->body->getPosition().y;
						bone->moveamount++;
						bone->body->setPosition(newPos);
					}
					else{
						bone->atDestination(cyclone::Vector3(bone_position[k][0]-ENEMY_X[ran3],bone_position[k][1],bone_position[k][2]-ENEMY_Z[ran4]));
					
						bone->setVelocity(cyclone::Vector3(( bone->chosenDestination.x-bone->body->getPosition().x)/(bone->max_moveamount*vel_multiply),
										bone->body->getPosition().y,
										(bone->chosenDestination.z-bone->body->getPosition().z)/(bone->max_moveamount*vel_multiply)));
						bone->moveamount = 0;
					}}
					bone->body->integrate(duration);
					bone->calculateInternals();

		}
	}
///<ExplosionUpdate
}


void CoconutGame::initGraphics(){
    GLfloat lightAmbient[] = {0.8,0.8,0.8,1.0};
    GLfloat lightDiffuse[] = {0.9,0.95,1.0,1.0};

    glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);

    glEnable(GL_LIGHT0);

    Application::initGraphics();
}

void CoconutGame::display(){

	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	gluPerspective(60.0f, (GLfloat)this->width  / (GLfloat)this->width  , 1, 500);
		gluLookAt(0.0, 3.5, 15.0,  0.0, 3.5, 0.0,  0.0, 1.0, 0.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW) ;
	glLoadIdentity(); 

	const static GLfloat lightPosition[] = {1,-1,0,0};
	const static GLfloat lightPositionMirror[] = {1,1,0,0};

	//CALC INTERNALS OF OBJECTS PRE RENDER
    for (Box *box = boxData; box < boxData+boxes; box++){
        box->calculateInternals();
    }
	if(floor_ice){
		for(int i = 0;i<snow->y;i++){
			for(int k = 0; k < snow->x;k++){
				for(int j = 0; j < snow->z;j++){
					snow->snow[i][k][j]->calculateInternals();
				}
			}
		}
	}
    for (Ball *ball = ballData; ball < ballData+balls; ball++){
        ball->calculateInternals();
    }

	cyclone::Random random;
	int chooseEnemyToAttack = random.randomInt(player_amount);

	for(int i = 0; i < enemies_Bones->size();i++){
			for(int j = 0; j < enemies_Bones->at(i)->size();j++){
				Bone *bone = enemies_Bones->at(i)->at(j);
				bone->calculateInternals();		
			}
	}

    // Clear the viewport and set the camera direction
	RigidBodyApplication::display();

    // Render each element in turn as a shadow
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
	glPushMatrix();
    glMultMatrixf(floorMirrorTranspose);
    glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
    glColor3f(1,0,0);


	//RENDER SHADOWS OF ALL OBJECTS


    for (Ball *ball = ballData; ball < ballData+balls; ball++){
        ball->render();
    }

	if(floor_ice){
		for(int i = 0;i<snow->y;i++){
			for(int k = 0; k < snow->x;k++){
				for(int j = 0; j < snow->z;j++){
					snow->snow[i][k][j]->render();
				}
			}
		}
	}

	for(int i = 0; i < enemies_Bones->size();i++){
		for(int j = 0; j < enemies_Bones->at(i)->size();j++){
				Bone *bone = enemies_Bones->at(i)->at(j);
				bone->render();
		}
	}

	//DRAW JOINTS FOR RAGDOLL
	glDisable(GL_DEPTH_TEST);
	glBegin(GL_LINES);
	for(int j = 0; j < enemies_Joints->size();j++){
	for (unsigned i = 0; i < enemies_Joints->at(j)->size(); i++)
	{
		cyclone::Joint *joint = enemies_Joints->at(j)->at(i);
		cyclone::Vector3 a_pos = joint->body[0]->getPointInWorldSpace(joint->position[0]);
		cyclone::Vector3 b_pos = joint->body[1]->getPointInWorldSpace(joint->position[1]);
		cyclone::real length = (b_pos - a_pos).magnitude();
		if (length > joint->error) glColor3f(1,0,0);
		else glColor3f(0,1,0);

		glVertex3f(a_pos.x, a_pos.y, a_pos.z);
		glVertex3f(b_pos.x, b_pos.y, b_pos.z);
	}}
	glEnd();
	glEnable(GL_DEPTH_TEST);
    glPopMatrix();
    glDisable(GL_LIGHTING);
    glDisable(GL_COLOR_MATERIAL);

    // Draw some scale circles
    glColor3f(0.75, 0.75, 0.75);
    for (unsigned i = 1; i < 50; i++){
        glBegin(GL_LINE_LOOP);
        for (unsigned j = 0; j < 32; j++){
            float theta = 3.1415926f * j / 16.0f;
            glVertex3f(i*cosf(theta),0.0f,i*sinf(theta));
        }
        glEnd();
    }
    glBegin(GL_LINES);
    glVertex3f(-20,0,0);
    glVertex3f(20,0,0);
    glVertex3f(0,0,-20);
    glVertex3f(0,0,20);
    glEnd();

	// DRAWING ATTACK AREA
	glEnable(GL_BLEND);
    if(floor_normal)glColor4f(1,1,0,0.5f);
	else if(floor_ice)glColor4f(0.7f,0.7f,0.7f,0.5f);
	else if(floor_tar)glColor4f(1,0.5f,0.5f,0.5f);
    glDisable(GL_DEPTH_TEST);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	 glBegin(GL_QUADS);
			glVertex3f(-11, 0, -20);  
			glVertex3f(-00, 0, -20);   
			glVertex3f(-00, 0, 20);      		
			glVertex3f(-11, 0, 20);    
    glEnd();
	glColor3f(0.75, 0.75, 0.75);
	glDisable(GL_BLEND);
    glEnable(GL_DEPTH_TEST);

    // Render each shadow in turn

	glEnable(GL_BLEND);
    glColor4f(0,0,0,0.1f);
    glDisable(GL_DEPTH_TEST);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
    for (Box *box = boxData; box < boxData+boxes; box++){
        box->renderShadow();
    }
    for (Ball *ball = ballData; ball < ballData+balls; ball++){
        ball->renderShadow();
    }

	if(floor_ice){
		for(int i = 0;i<snow->y;i++){
			for(int k = 0; k < snow->x;k++){
				for(int j = 0; j < snow->z;j++){
					snow->snow[i][k][j]->renderShadow();
				}
			}
		}
	}

	for(int i = 0; i < enemies_Bones->size();i++){
		for(int j = 0; j < enemies_Bones->at(i)->size();j++){
			Bone *bone = enemies_Bones->at(i)->at(j);
			bone->renderShadow();
		}
	}
	glDisable(GL_BLEND);

    //RENDER ALL OTHER OBJECTS
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPositionMirror);
    glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);

    for (Box *box = boxData; box < boxData+boxes; box++){
        box->render();
    }
    for (Ball *ball = ballData; ball < ballData+balls; ball++){
        ball->render();
    }

	if(floor_ice){
		for(int i = 0;i<snow->y;i++){
			for(int k = 0; k < snow->x;k++){
				for(int j = 0; j < snow->z;j++){
					snow->snow[i][k][j]->render();
				}
			}
		}
	}

	for(int i = 0; i < enemies_Bones->size();i++){
		for(int j = 0; j < enemies_Bones->at(i)->size();j++){
			Bone *bone = enemies_Bones->at(i)->at(j);
			bone->render();
		}
	}
		
	glDisable(GL_DEPTH_TEST);
	glBegin(GL_LINES);
	for(int j = 0; j < enemies_Joints->size();j++){
	for (unsigned i = 0; i < enemies_Joints->at(j)->size(); i++)
	{
		cyclone::Joint *joint = enemies_Joints->at(j)->at(i);// + i;
		cyclone::Vector3 a_pos = joint->body[0]->getPointInWorldSpace(joint->position[0]);
		cyclone::Vector3 b_pos = joint->body[1]->getPointInWorldSpace(joint->position[1]);
		cyclone::real length = (b_pos - a_pos).magnitude();
		if (length > joint->error) glColor3f(1,0,0);
		else glColor3f(0,1,0);

		glVertex3f(a_pos.x, a_pos.y, a_pos.z);
		glVertex3f(b_pos.x, b_pos.y, b_pos.z);
	}}
	glEnd();
	glEnable(GL_DEPTH_TEST);
    glDisable(GL_COLOR_MATERIAL);
    glDisable(GL_LIGHTING);
    glDisable(GL_DEPTH_TEST);

    // Finish the frame, rendering any additional information
	UI();//DRAW UI
	//drawDebug();
	//std::cout << "hittable_enemy_amount: " << hittable_enemy_amount << " player_amount " << player_amount << " playerHits " << playerHits << "\n";
	//std::cout << "Normal: " << floor_normal << " ICE: " << floor_ice << " TAR: " << floor_tar << "\n";
	//std::cout << "Lives: " << lives << " points: " << playerScore << "\n";
}

//METHOD TO DETECT KEY PRESSES, SETS BALL VELOCITY AND OTHER GAME UI COMMANDS
void CoconutGame::key(){
	//std::cout << " Z: " << playerZposition << " Y: " << playerYposition << " V: " << playerVelocity << "\n";
	pause(0.01);
	//do time based while loop
	if(glfwGetKey('A') == GLFW_PRESS && !playerZpositionCap) {
		playerZposition += 0.1;
		if(playerZposition >= 20) playerZposition = 19.5;
        return;
	}

	if(glfwGetKey('D') == GLFW_PRESS && !playerZpositionCap) {
		playerZposition -= 0.1;
		if(playerZposition <= -20) playerZposition = -19.5;
        return;
	}

	if(glfwGetKey('P') == GLFW_PRESS && !playerVelocityCap) {
		playerVelocity -= 0.1;
		if(playerVelocity <= -40) playerVelocity = -39.5;
        return;
	}

	if(glfwGetKey('O') == GLFW_PRESS && !playerVelocityCap) {
		playerVelocity += 0.1;
		if(playerVelocity > 0) playerVelocity = -1;
        return;
	}

	if(glfwGetKey('W') == GLFW_PRESS && !playerYpositionCap) {
		playerYposition += 0.1;
		if(playerYposition >= 20) playerYposition = 19.5;
        return;
	}

	if(glfwGetKey('S') == GLFW_PRESS && !playerYpositionCap) {
		playerYposition -= 0.1;
		if(playerYposition <= -20) playerYposition = -19.5;
        return;
	}
	
	//FIRES A BALL INTO THE NEWLY SET TRAJECTORY
	if(glfwGetKey('F') == GLFW_PRESS && ballHitSomething) {
		if(ballcounter > sizeof(&ballData))ballcounter = 0;
		Ball* ball = &ballData[0];//ballcounter
			cyclone::Vector3 pos(18,5,0);
			cyclone::Vector3 vel(playerVelocity,playerYposition+0.1f,playerZposition);
			ball->setState(
				pos,
				cyclone::Quaternion(),
				1.0f,
				vel
			);
			ball->body->setAcceleration(cyclone::Vector3::GRAVITY);
			ball->BALL_COLLIDE = false;
		ballcounter++;
        return;
	}
    RigidBodyApplication::key();
}

Application* getApplication()
{
    return new CoconutGame();
} 

//DRAWS THE GAME UI
//DRAWS 5 BARS WHICH FILL WITH UNIQUE COLOURS BASED
//ON SET VARIBLES
void CoconutGame::UI(){
	glPushMatrix();
		glMatrixMode (GL_PROJECTION);
		glLoadIdentity();
		glOrtho(-1,1,1,1, 1, 500);
		glMatrixMode(GL_MODELVIEW) ;
		glLoadIdentity(); 
		glPushMatrix();
			glColor3f(1,0,0);
			glBegin(GL_QUADS);
			{
				float f = playerVelocity*0.025f;
				f*=-1;
				f-=0.5;

				if(f < -0.5){
					f = -0.5;
					
					playerVelocityCap = true;
				} else if(f > 0.5){
					f = 0.5;
					playerVelocityCap = true;
				} else playerVelocityCap = false; 
			
				glVertex3f(f, 0.925, 0);  
				glVertex3f(f, 0.9, 0);  
				glVertex3f(-0.5, 0.9, 0); 
				glVertex3f(-0.5, 0.925, 0);  
			}
			glEnd();
			glColor3f(0,0,0);
			glLineWidth(0.5f);
			glBegin(GL_LINES);
			{
				glVertex3f(0.5, 0.925, 0);  
				glVertex3f(-0.5, 0.925, 0);  

				glVertex3f(0.5, 0.9, 0);  
				glVertex3f(-0.5, 0.9, 0);  

				glVertex3f(-0.5, 0.9, 0); 
				glVertex3f(-0.5, 0.925, 0); 

				glVertex3f(0.5, 0.925, 0);  
				glVertex3f(0.5, 0.9, 0);  

			}
			glEnd();
		glPopMatrix();
		glPushMatrix();
			glBegin(GL_QUADS);
			{//playerZpositionCap
				float f = playerZposition*0.025f;

				if(f < -0.5){
					f = -0.5;
					playerZpositionCap = true;
				} else if(f > 0.5){
					f = 0.5;
					playerZpositionCap = true;
				} else playerVelocityCap = false; 

				if(f < 0)glColor3f(1,1,0);
				else glColor3f(0,1,0);
				f*=-1;
				glVertex3f(f, 0.9, 0);  
				glVertex3f(f, 0.875, 0);  
				glVertex3f(0, 0.875, 0); 
				glVertex3f(0, 0.9, 0);  
			}
			glEnd();
			glColor3f(0,0,0);
			glLineWidth(0.5f);
			glBegin(GL_LINES);
			{
				glVertex3f(0.5, 0.9, 0);  
				glVertex3f(-0.5, 0.9, 0);  

				glVertex3f(0.5, 0.875, 0);  
				glVertex3f(-0.5, 0.875, 0);  

				glVertex3f(-0.5, 0.875, 0); 
				glVertex3f(-0.5, 0.9, 0); 

				glVertex3f(0.5, 0.9, 0);  
				glVertex3f(0.5, 0.875, 0);  

			}
			glEnd();
		glPopMatrix();
		glPushMatrix();
			glBegin(GL_QUADS);
			{
				float f = playerYposition*0.025f;

				if(f < -0.5){
					f = -0.5;
					playerYpositionCap = true;
				} else if(f > 0.5){
					f = 0.5;
					playerYpositionCap = true;
				} else playerVelocityCap = false; 

				if(f < 0)glColor3f(0,0,1);
				else glColor3f(0,1,1);
				f*=-1;
				glVertex3f(f, 0.875, 0);  
				glVertex3f(f, 0.85, 0);  
				glVertex3f(0, 0.85, 0); 
				glVertex3f(0, 0.875, 0);  
			}
			glEnd();
			glColor3f(0,0,0);
			glLineWidth(0.5f);
			glBegin(GL_LINES);
			{
				glVertex3f(0.5, 0.875, 0);  
				glVertex3f(-0.5, 0.875, 0);  

				glVertex3f(0.5, 0.85, 0);  
				glVertex3f(-0.5, 0.85, 0);  

				glVertex3f(-0.5, 0.85, 0); 
				glVertex3f(-0.5, 0.875, 0); 

				glVertex3f(0.5, 0.875, 0);  
				glVertex3f(0.5, 0.85, 0);  

			}
			glEnd();
		glPopMatrix();
				//### HEALTH BAR
		glPushMatrix();
			glBegin(GL_QUADS);
			{

				float f = -0.5f;
				if(lives >= 10)f = 0.5f;
				else if(lives == 9)f = 0.4f;
				else if(lives == 8)f = 0.3f;
				else if(lives == 7)f = 0.2f;
				else if(lives == 6)f = 0.1f;
				else if(lives == 5)f = 0.0f;
				else if(lives == 4)f = -0.1f;
				else if(lives == 3)f = -0.2f;
				else if(lives == 2)f = -0.3f;
				else if(lives == 1)f = -0.4f;
				
				glColor3f(1,0.5,0.5);

				glVertex3f(f, 0.825, 0);  
				glVertex3f(f, 0.85, 0);  
				glVertex3f(-0.5, 0.85, 0); 
				glVertex3f(-0.5, 0.825, 0);  
			}
			glEnd();
			glColor3f(0,0,0);
			glLineWidth(0.5f);
			glBegin(GL_LINES);
			{
				glVertex3f(0.5, 0.825, 0);  
				glVertex3f(-0.5, 0.825, 0);  

				glVertex3f(0.5, 0.85, 0);  
				glVertex3f(-0.5, 0.85, 0);  

				glVertex3f(-0.5, 0.85, 0); 
				glVertex3f(-0.5, 0.825, 0); 

				glVertex3f(0.5, 0.825, 0);  
				glVertex3f(0.5, 0.85, 0);  

			}
			glEnd();
		glPopMatrix();
		//### point BAR
		glPushMatrix();
			glBegin(GL_QUADS);
			{
				float f = -0.5f;
				float ps = playerScore;
				if(ps >= 0){
					glColor3f(0,0,1);
					for(int i = 0; i < ps; i++){
						f += 0.025;
					}
				}
				else{
					glColor3f(1,0,0);
					for(int i = 0; i > ps; i--){
						f += 0.025;
					}
				}
				glVertex3f(f, 0.8, 0);  
				glVertex3f(f, 0.825, 0);  
				glVertex3f(-0.5, 0.825, 0); 
				glVertex3f(-0.5, 0.8, 0);  
			}
			glEnd();
			glColor3f(0,0,0);
			glLineWidth(0.5f);
			glBegin(GL_LINES);
			{
				glVertex3f(0.5, 0.8, 0);  
				glVertex3f(-0.5, 0.8, 0);  

				glVertex3f(0.5, 0.825, 0);  
				glVertex3f(-0.5, 0.825, 0);  

				glVertex3f(-0.5, 0.825, 0); 
				glVertex3f(-0.5, 0.8, 0); 

				glVertex3f(0.5, 0.8, 0);  
				glVertex3f(0.5, 0.825, 0);  

			}
			glEnd();
		glPopMatrix();

	glPopMatrix();
}